package com.nidu.demo.menu.gatewayimpl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cola.dto.PageResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nidu.demo.common.constants.Constants;
import com.nidu.demo.menu.convertor.MenuConvertor;
import com.nidu.demo.menu.gateway.MenuGateway;
import com.nidu.demo.menu.mapper.MenuMapper;
import com.nidu.demo.menu.mapper.dataobject.MenuDO;
import com.nidu.demo.menu.model.Menu;
import com.nidu.demo.menu.model.MenuCriteria;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
public class MenuGatewayImpl implements MenuGateway {
    
    private final MenuMapper mapper;
    private final MenuConvertor convertor;

    @Override
    public Menu create(Menu menu) {
        MenuDO menuDO = convertor.toDataObject(menu);
        mapper.insert(menuDO);
        return convertor.toDomainObject(menuDO);
    }

    @Override
    public Menu update(Menu menu) {
        MenuDO menuDO = convertor.toDataObject(menu);
        mapper.updateById(menuDO);
        return convertor.toDomainObject(menuDO);
    }

    @Override
    public Menu getById(Long id) {
        MenuDO menuDO = mapper.selectById(id);
        if(menuDO == null){
            return null;
        }
        return convertor.toDomainObject(menuDO);
    }

    @Override
    public List<Menu> getByIds(Set<Long> ids) {
        LambdaQueryWrapper<MenuDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(MenuDO::getId, ids);
        List<MenuDO> menuDOS = mapper.selectList(wrapper);
        if(CollUtil.isEmpty(menuDOS)){
            return new ArrayList<>();
        }
        return convertor.toDomainObjects(menuDOS);
    }

    @Override
    public Menu getByName(String name) {
        MenuDO menuDO = mapper.selectByName(name);
        if(menuDO == null){
            return null;
        }
        return convertor.toDomainObject(menuDO);
    }

    @Override
    public List<Menu> getByParentId(Long parentId) {
        List<MenuDO> menuDOs = mapper.selectByParentId(parentId);
        if(CollUtil.isEmpty(menuDOs)){
            return null;
        }
        return convertor.toDomainObjects(menuDOs);
    }

    @Override
    public void deleteById(Long id) {
        mapper.deleteById(id);
    }

    @Override
    public void deleteByIds(Set<Long> ids) {
        mapper.deleteByIds(ids);
    }

    @Override
    public List<Menu> listByCondition(MenuCriteria criteria) {
        LambdaQueryWrapper<MenuDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(criteria.getStatus() != null, MenuDO::getStatus, criteria.getStatus());
        wrapper.in(!CollUtil.isEmpty(criteria.getMenuIds()), MenuDO::getId, criteria.getMenuIds());

        wrapper.like(!StrUtil.isEmpty(criteria.getMenuName()), MenuDO::getName, criteria.getMenuName());
        wrapper.eq(criteria.getParentId() != null, MenuDO::getParentId, criteria.getParentId());
        wrapper.eq(criteria.getMenuType() != null, MenuDO::getType, criteria.getMenuType());
        List<MenuDO> menuDOS = mapper.selectList(wrapper);
        if(CollUtil.isEmpty(menuDOS)){
            return new ArrayList<>();
        }
        return convertor.toDomainObjects(menuDOS);
    }

    @Override
    public List<Menu> treeByCondition(MenuCriteria qry) {
        // 1. 查询所有菜单
        LambdaQueryWrapper<MenuDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(qry.getStatus() != null, MenuDO::getStatus, qry.getStatus());
        LocalDateTime[] createTime = qry.getCreateTime();
        if (createTime != null) {
            if (createTime.length == 1 && createTime[0] != null) {
                wrapper.ge(MenuDO::getCreateTime, createTime[0]);
            } else if (createTime.length > 1 && createTime[0] != null && createTime[1] != null) {
                wrapper.between(MenuDO::getCreateTime, createTime[0], createTime[1]);
            }
        }
        wrapper.like(!StrUtil.isEmpty(qry.getMenuName()), MenuDO::getName, qry.getMenuName());

        List<MenuDO> menuDOS = mapper.selectList(wrapper);
        if(CollUtil.isEmpty(menuDOS)){
            return new ArrayList<>();
        }
        return convertor.toDomainObjects(menuDOS);
    }

    @Override
    public PageResponse<Menu> pageByCondition(MenuCriteria qry) {
        // 1. 查询所有菜单
        LambdaQueryWrapper<MenuDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(qry.getStatus() != null, MenuDO::getStatus, qry.getStatus());

        LocalDateTime[] createTime = qry.getCreateTime();
        if (createTime != null) {
            if (createTime.length == 1 && createTime[0] != null) {
                wrapper.ge(MenuDO::getCreateTime, createTime[0]);
            } else if (createTime.length > 1 && createTime[0] != null && createTime[1] != null) {
                wrapper.between(MenuDO::getCreateTime, createTime[0], createTime[1]);
            }
        }

        List<MenuDO> menuDOS = mapper.selectList(wrapper);
        if(CollUtil.isEmpty(menuDOS)){
            return PageResponse.of(qry.getPageSize(), qry.getPageIndex());
        }
        List<Menu> menuList = convertor.toDomainObjects(menuDOS);

        // 2. 找出所有根节点（parentId = 0）
        List<Menu> rootMenus = menuList.stream()
                .filter(menu -> Constants.ROOT_MENU_ID.equals(menu.getParentId()))
                .sorted(Comparator.comparing(Menu::getSortOrder))
                .collect(Collectors.toList());

        int total = rootMenus.size();
        int fromIndex = Math.min((qry.getPageIndex() - 1) * qry.getPageSize(), total);
        int toIndex = Math.min(fromIndex + qry.getPageSize(), total);
        if (fromIndex >= toIndex) {
            return PageResponse.of(qry.getPageSize(), qry.getPageIndex());
        }

        // 3. 获取分页根节点
        List<Menu> pagedRoots = rootMenus.subList(fromIndex, toIndex);

        // 4. 查找所有子孙节点
        Set<Long> rootIds = pagedRoots.stream().map(Menu::getId).collect(Collectors.toSet());
        List<Menu> descendants = findDescendants(menuList, rootIds);

        // 5. 合并成最终返回数据
        List<Menu> pageData = new ArrayList<>();
        pageData.addAll(pagedRoots);
        pageData.addAll(descendants);

        return PageResponse.of(pageData, rootMenus.size(), qry.getPageSize(), qry.getPageIndex());
    }

    @Override
    public void updateStatus(Long id, Integer status) {
        MenuDO menuDO = new MenuDO();
        menuDO.setId(id);
        menuDO.setStatus(status);
        mapper.updateById(menuDO);
    }

    private List<Menu> findDescendants(List<Menu> allMenus, Set<Long> parentIds) {
        List<Menu> descendants = new ArrayList<>();
        Queue<Long> queue = new LinkedList<>(parentIds);

        Set<Long> visited = new HashSet<>();
        while (!queue.isEmpty()) {
            Long parentId = queue.poll();
            for (Menu menu : allMenus) {
                if (Objects.equals(menu.getParentId(), parentId) && !visited.contains(menu.getId())) {
                    descendants.add(menu);
                    queue.add(menu.getId());
                    visited.add(menu.getId());
                }
            }
        }
        return descendants;
    }
} 